/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.user.client;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.UIObject;

/**
 * A default pending handler to process pending state by showing a pending icon.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class DefaultPendingHandler implements PendingHandler {
  /**
   * A ClientBundle that provides images for this widget.
   */
  public interface Resources extends ClientBundle {

    @Source("pending.gif")
    ImageResource pending();
  }

  /**
   * The default blocked pending handler.
   */
  public static PendingHandler BLOCKED = new PendingHandler() {
    private PopupPanel pending;

    @Override
    public void onFinish() {
      if (pending != null) {
        pending.hide();
      }
    }

    @Override
    public void onPending() {
      if (pending == null) {
        pending = pending();
      } else {
        pending.center();
      }
    }

    /**
     * Shows a pending element with modal state.
     * 
     * @return the {@link PopupPanel}
     */
    private PopupPanel pending() {
      PopupPanel popup = new PopupPanel();
      popup.setWidget(new Image(resources.pending()));
      popup.setAutoHideOnHistoryEventsEnabled(true);
      popup.setAutoHideEnabled(false);
      popup.setModal(true);
      popup.setStyleName(null);
      popup.getElement().getStyle().setZIndex(Integer.MAX_VALUE);
      popup.center();
      return popup;
    }
  };

  private static Resources resources = GWT.create(Resources.class);

  /**
   * Gets the resources.
   * 
   * @return the resources
   */
  public static ImageElement createPendingIndicator() {
    ImageElement indicator = Document.get().createImageElement();
    indicator.setSrc(resources.pending().getSafeUri().asString());
    return indicator;
  }

  /**
   * Puts a pending element at the center of the given targets.
   * 
   * @param target the target
   * @return the appended element
   */
  public static Element pending(final UIObject target) {
    assert target != null : "Target must not be null";
    ImageElement indicator = Document.get().createImageElement();
    Element container = target.getElement().getParentElement();
    indicator.setSrc(resources.pending().getSafeUri().asString());
    indicator.getStyle().setDisplay(Display.INLINE_BLOCK);
    indicator.getStyle().setPosition(Position.ABSOLUTE);

    container.insertBefore(indicator, target.getElement());
    // calculates offset after appending indicator
    // make the origin position be same with target's
    int origin_x = indicator.getAbsoluteLeft() - target.getAbsoluteLeft();
    int origin_y = indicator.getAbsoluteTop() - target.getAbsoluteTop();
    // for centralize
    int offset_x = (target.getOffsetWidth() - indicator.getOffsetWidth()) >> 1;
    int offset_y = (target.getOffsetHeight() - indicator.getOffsetHeight()) >> 1;
    // shift to middle-center
    indicator.getStyle().setMarginLeft(-origin_x + offset_x, Unit.PX);
    indicator.getStyle().setMarginTop(-origin_y + offset_y, Unit.PX);
    return indicator;
  }

  private Element pendingIndicator;

  private final UIObject pendingTarget;

  /**
   * Creates a pending handler with the given target.
   * 
   * @param pendingTarget the pending target to be covered with a pending state.
   */
  public DefaultPendingHandler(UIObject pendingTarget) {
    this.pendingTarget = pendingTarget;
  }

  @Override
  public void onFinish() {
    if (pendingIndicator != null) {
      pendingIndicator.removeFromParent();
      if (pendingTarget instanceof HasEnabled) {
        ((HasEnabled) pendingTarget).setEnabled(true);
      }
      pendingIndicator = null;
    }
  }

  @Override
  public void onPending() {
    // ensure finished
    if (pendingIndicator != null) {
      onFinish();
    }
    pendingIndicator = pending(pendingTarget);
    if (pendingTarget instanceof HasEnabled) {
      ((HasEnabled) pendingTarget).setEnabled(false);
    }
  }
}